/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.core;
import java.net.URL;
import java.net.URLConnection;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.io.*;
import java.text.MessageFormat;
import java.util.*;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import org.xml.sax.AttributeList;
import org.openide.filesystems.FileObject;
import org.openide.modules.ModuleInstall;
import org.openide.modules.ManifestSection;
import org.openide.modules.ModuleDescription;
import org.openide.modules.IllegalModuleException;
import org.openide.TopManager;
import org.openide.loaders.DataLoader;
import org.openide.util.NbBundle;
import org.openide.util.datatransfer.ExClipboard;
/**
* Item representing one module.
*
* @author Jaroslav Tulach, Petr Hrebejk
*/
class ModuleItem extends Object {
/** Constants for module bases */
static final int BASE_USER = 1;
static final int BASE_CENTRAL = 2;
static final int BASE_JAR = 3;
/** is enabled or not */
boolean enabled;
/** URL for the class loader */
private transient URL loaderURL;
/** Old release number of the module */
private transient int oldRelease = -1;
/** Old specification version of the module */
private transient String oldSpecVersion = null;
/** Description of the module */
transient ModuleDescription descr;
/** The module was deleted from autoload folder */
private transient boolean deleted = false;
/** Base of the module local|cenral|file */
private int base = BASE_USER;
/** The jar file the module is loaded from - used for modules
* with base == BASE_CENTRAL || BASE_USER || BASE_JAR )
*/
private File moduleFile = null;
/** Whether or not, in this session, the $NBHOME/docs/ dir has been added
* to the system classloader to help find help sets.
*/
private static boolean addedDocsDir = false;
/** Just to be able to have modules without jar file */
protected ModuleItem() {
}
/** Creates new module for Jar file.
* @param base Base of the module BASE_USER || BASE_CENTRAL || BASE_JAR
* @param name path to the module jar
* @param enabled enabled?
* @exception IllegalModuleException if the jar is not module
* @exception IOException if an error occures during reading
*/
public ModuleItem (int base, String location, boolean enabled)
throws IllegalModuleException, FileNotFoundException,
MalformedURLException, IOException {
this.enabled = enabled;
this.base = base;
File file = null;
switch( base ) {
case BASE_USER:
file = new File( ModuleInstallerSupport.getUserModuleDirectory(), location );
break;
case BASE_CENTRAL:
file = new File( ModuleInstallerSupport.getCentralModuleDirectory(), location );
break;
case BASE_JAR:
file = new File( location );
break;
default:
throw new InternalError( "Unknown module base " ); // NOI18N
}
if ( !file.canRead() ) {
throw new FileNotFoundException( "Can't find module jarfile : " + file.toString() ); // NOI18N
}
moduleFile = file;
JarFile jarFile = new JarFile( file );
Manifest manifest = jarFile.getManifest();
jarFile.close();
//loaderURL = new URL( "jar:" + file.toURL().toExternalForm() + "!/" ); // NOI18N
loaderURL = file.toURL();
init (manifest, file.getName(), enabled);
}
/** Initializes new module item according to given manifiest
* @param manifest Manifest of the module
* @param name Name to initialize the ModuleDescription
* @param enabled enabled?
* @exception IllegalModuleException if the jar is not module
* @exception IOException if an error occures during reading
*/
protected void init (Manifest manifest, String name, boolean enabled)
throws IllegalModuleException {
this.enabled = enabled;
this.descr = new ModuleDescription ( name, manifest);
}
/** @param en true if enabled
*/
public void setEnabled (boolean en) {
if (enabled == en) return;
enabled = en;
ModuleInstaller.changeEnabled (this);
}
void setEnabledNoNotify( boolean en ) {
enabled = en;
}
/** @return true if enabled
*/
public boolean isEnabled () {
return enabled;
}
/** @return Representation of ModuleItem location in XML file */
public String getLocationForXML () {
switch ( base ) {
case BASE_CENTRAL:
case BASE_USER:
return moduleFile.getName();
case BASE_JAR:
return moduleFile.getPath();
}
return loaderURL.toString();
}
/** @return URL to be used by class loaders */
public URL getLoaderURL () {
return loaderURL;
}
/** @return Release of the module from previous start of the Ide or -1 */
public int getOldRelease() {
return oldRelease;
}
/** Old release is set only by ModuleInstaller */
void setOldRelease( int oldRelease ) {
this.oldRelease = oldRelease;
}
/** @return Specification version of the module from previous start
* of the IDE or <CODE>null</CODE> */
public String getOldSpecVersion() {
return oldSpecVersion;
}
/** Old release is set only by ModuleInstaller */
void setOldSpecVersion( String oldSpecVersion ) {
this.oldSpecVersion = oldSpecVersion;
}
/** Test if the module was updated after last shutdown of IDE
*/
boolean isUpdated() {
if ( descr.getCodeNameRelease() > oldRelease ) {
return true;
}
try {
if ( !ModuleDescription.compatibleWith( descr.getSpecVersion(), oldSpecVersion ) ) {
return true;
}
}
catch ( org.openide.modules.IllegalModuleException e ) {
TopManager.getDefault().notifyException( e );
}
return false;
}
/** Was the module deleted? */
/*
boolean isDeleted() {
return deleted;
}
*/
/** Sets the deleted value of the module item */
/*
void setDeleted( boolean deleted ) {
this.deleted = deleted;
}
*/
/** Getter for the module base */
public int getBase() {
return base;
}
/** Getter for description */
public ModuleDescription getDescription() {
return descr;
}
/** Is the deletion of this item allowed */
public boolean canDestroy() {
return base != BASE_CENTRAL && base != BASE_USER;
}
/** Get the actual URL to point to this module's help set.
* If there is a valid URL from the JAR itself (i.e. according to
* ModuleDescription), then that of course is used. If not, we also
* look in the docs/ folder before giving up.
* @return the URL to the help set file, or null if no help set was specified, or if it could not be found
*/
private URL getRealHelpSet () {
try {
return descr.getDescription ();
} catch (IllegalStateException ise) {
synchronized (ModuleItem.class) {
if (addedDocsDir) {
TopManager.getDefault ().notifyException (ise);
return null;
}
// Now try docs/.
String nbhome = System.getProperty ("netbeans.home");
if (nbhome != null) {
try {
// Java bug: if you do not canonicalize, it will make an absolute path,
// however file:/d:/nbdir/./docs/ is not treated as a valid URL for the
// URLClassLoader, for some reason.
URL url = new File (nbhome, "docs").getCanonicalFile ().toURL (); // NOI18N
if (! url.toString ().endsWith ("/")) // NOI18N
url = new URL (url.toString () + "/"); // NOI18N
ModuleClassLoader.add (url);
} catch (Exception e) {
if (Boolean.getBoolean ("netbeans.debug.exceptions")) // NOI18N
e.printStackTrace ();
}
}
String nbuser = System.getProperty ("netbeans.user");
if (nbuser != null && ! nbuser.equals (nbhome)) {
try {
URL url = new File (nbuser, "docs").getCanonicalFile ().toURL (); // NOI18N
if (! url.toString ().endsWith ("/")) // NOI18N
url = new URL (url.toString () + "/"); // NOI18N
ModuleClassLoader.add (url);
} catch (Exception e) {
if (Boolean.getBoolean ("netbeans.debug.exceptions")) // NOI18N
e.printStackTrace ();
}
}
addedDocsDir = true;
}
try {
return descr.getDescription ();
} catch (IllegalStateException ise2) {
TopManager.getDefault ().notifyException (ise2);
return null;
}
}
}
/** Initializes defaults.
*/
public void restoreDefault () {
URL helpSet = getRealHelpSet ();
if (helpSet != null) {
Help.getDefault ().addHelpSet (helpSet, descr.getCodeName (), descr.getName ());
}
}
/** Deinitializes them.
*/
public void unrestoreDefault () {
URL helpSet = getRealHelpSet ();
if (helpSet != null) {
Help.getDefault ().removeHelpSet (helpSet);
}
}
/** Called when the module is installed for the first time
*/
public void installSection () {
ModuleActions.attachTo (this);
descr.forEachSection (INSTALL);
ModuleActions.attachTo (null);
}
/** Called when the module is restored on startup
*/
public void restoreSection () {
ModuleActions.attachTo (this);
descr.forEachSection (RESTORE);
ModuleActions.attachTo (null);
}
/** Called when the module should be uninstalled.
*/
public void uninstallSection () {
ModuleActions.attachTo (this);
descr.forEachSection (REMOVE);
ModuleActions.attachTo (null);
}
/** Called when the module is installed for the first time
*/
public void installCode () {
descr.getModule ().installed ();
}
/** Called when the module is restored on startup
*/
public void restoreCode () {
descr.getModule ().restored ();
}
/** Called when the module should be uninstalled.
*/
public void uninstallCode () {
descr.getModule ().uninstalled ();
}
/** Called when the module was updated to higher version.
*/
public void updatedCode() {
descr.getModule ().updated( oldRelease, oldSpecVersion );
}
/** Called when the IDE is closing
* @return false if the IDE should not close
*/
public boolean closing () {
try {
return descr.getModule ().closing ();
} catch (RuntimeException ex) {
// if there is an error, suppose the module is completelly bad.
return true;
}
}
/** Called when the IDE is finallty closing. All modules accepted the close
* of the IDE
*/
public void closeCode() {
descr.getModule ().close();
}
/** Mehod called by ModuleInstaller to get the XML format of ModuleItem
*/
String toXML() {
StringBuffer moduleBuffer = new StringBuffer( 150 );
/*
if ( isDeleted() && !( base == BASE_CENTRAL || base == BASE_USER ) )
return moduleBuffer.toString();
*/
// Module header
moduleBuffer.append( ModuleTags.TAB ).append("<" ).append( ModuleTags.MODULE ); // NOI18N
moduleBuffer.append( ModuleTags.NEW_LINE );
// CodeBaseName
moduleBuffer.append( ModuleTags.TABx2 ).append( ModuleTags.CODENAMEBASE ).append( "=\"" ); // NOI18N
moduleBuffer.append( getDescription().getCodeNameBase() ).append( "\"" ); // NOI18N
moduleBuffer.append( ModuleTags.NEW_LINE );
// Version
moduleBuffer.append( ModuleTags.TABx2 ).append( ModuleTags.RELEASE ).append( "=\"" ); // NOI18N
moduleBuffer.append( getDescription().getCodeNameRelease() ).append( "\"" ); // NOI18N
moduleBuffer.append( ModuleTags.TAB ).append( ModuleTags.SPECVERSION ).append( "=\"" ); // NOI18N
moduleBuffer.append( getDescription().getSpecVersion() ).append( "\"" ); // NOI18N
moduleBuffer.append( ModuleTags.NEW_LINE );
// Base
moduleBuffer.append( ModuleTags.TABx2 ).append( ModuleTags.BASE ).append( "=\"" ); // NOI18N
switch ( base ) {
case BASE_USER:
moduleBuffer.append( ModuleTags.BASE_USER );
break;
case BASE_CENTRAL:
moduleBuffer.append( ModuleTags.BASE_CENTRAL );
break;
case BASE_JAR:
moduleBuffer.append( ModuleTags.BASE_JAR );
break;
}
moduleBuffer.append( "\"" ).append( ModuleTags.NEW_LINE ); // NOI18N
// URL
moduleBuffer.append( ModuleTags.TABx2 ).append( ModuleTags.URL ).append( "=\"" ); // NOI18N
moduleBuffer.append( getLocationForXML() );
moduleBuffer.append( "\"" ); // NOI18N
moduleBuffer.append( ModuleTags.NEW_LINE );
// Enabled
moduleBuffer.append( ModuleTags.TABx2 ).append( ModuleTags.ENABLED ).append( "=\"" ); // NOI18N
moduleBuffer.append( new Boolean (isEnabled ()) ).append( "\"" ); // NOI18N
moduleBuffer.append( ModuleTags.NEW_LINE );
// Deleted
/*
if ( isDeleted() ) {
moduleBuffer.append( ModuleTags.TABx2 ).append( ModuleTags.DELETED ).append( "=\"" );
moduleBuffer.append( new Boolean ( isDeleted ()) ).append( "\"" );
moduleBuffer.append( ModuleTags.NEW_LINE );
}
*/
// Module footer
moduleBuffer.append( "/>"); // NOI18N
moduleBuffer.append( ModuleTags.NEW_LINE );
return moduleBuffer.toString();
}
/** Restores the item from XML. Creates new instance of ModuleItem
* represented by the attribute list.
* @return New ModuleItem or null if cannot be restored.
*/
static ModuleItem fromXML( AttributeList attr, URL base ) {
// Read main flags
String url = attr.getValue( ModuleTags.URL );
String enabled = attr.getValue( ModuleTags.ENABLED );
// String deleted = attr.getValue( ModuleTags.DELETED );
String baseAttr = attr.getValue( ModuleTags.BASE );
// Read old version of module
int relase;
try {
relase = Integer.parseInt( attr.getValue( ModuleTags.RELEASE ) );
}
catch ( NumberFormatException e ) {
relase = -1;
}
String specVersion = attr.getValue( ModuleTags.SPECVERSION );
// Restore the URL of module jar file
if ( url != null ) {
try {
int baseCode = BASE_CENTRAL;
if ( ModuleTags.BASE_USER.equals( baseAttr ) ) {
baseCode = BASE_USER;
}
else if ( ModuleTags.BASE_JAR.equals( baseAttr ) ) {
baseCode = BASE_JAR;
}
ModuleItem mi = new ModuleItem( baseCode, url,
enabled == null || Boolean.valueOf (enabled).booleanValue () );
/*
if ( deleted != null ) {
mi.setDeleted( Boolean.valueOf (deleted).booleanValue () );
}
*/
mi.setOldRelease( relase );
mi.setOldSpecVersion( specVersion );
return mi;
} catch (FileNotFoundException e) {
// simply ignore (Ian's request)
} catch (IOException e) {
TopManager.getDefault().notifyException(e);
}
}
return null;
}
/** Installs everything.
*/
private static class InstallIterator extends Object implements ManifestSection.Iterator {
/** Process action section.
*/
public void processAction (ManifestSection.ActionSection as) throws InstantiationException {
ModuleActions.add (as);
}
/** Processes the option section.
*/
public void processOption (ManifestSection.OptionSection os) throws InstantiationException {
NbControlPanel pool = NbControlPanel.getDefault ();
pool.add (os.getOption ());
}
/** Processes the loader section.
*/
public void processLoader (ManifestSection.LoaderSection ls) throws InstantiationException {
LoaderPoolNode.add (ls);
}
/** Processes debugger section
*/
public void processDebugger (ManifestSection.DebuggerSection ds) throws InstantiationException {
NbTopManager.setDebugger (ds.getDebugger ());
}
/** Processes executor section
*/
public void processService (ManifestSection.ServiceSection es) throws InstantiationException {
Services.addService (es);
}
/** Processes filesystem section
*/
public void processFileSystem (ManifestSection.FileSystemSection fs) throws InstantiationException {
ModuleFSSection.install (fs);
}
/** Processes environment node section.
* @exception Exception if there is an error and the section should be removed
*/
public void processNode (ManifestSection.NodeSection es) throws InstantiationException {
if (ManifestSection.NodeSection.TYPE_ROOTS.equalsIgnoreCase (es.getType ())) {
NbPlaces.addRoot (es.getNode ());
} else if (ManifestSection.NodeSection.TYPE_SESSION.equalsIgnoreCase (es.getType ())) {
NbPlaces.addSession (es.getNode ());
} else {
EnvironmentNode.addNode (es.getNode ());
}
}
/** Processes clipboard convertor section.
* @exception Exception if there is an error and the section should be removed
*/
public void processClipboardConvertor (ManifestSection.ClipboardConvertorSection es) throws InstantiationException {
CoronaClipboard.addConvertor (es.getConvertor ());
}
}
/** Installs new modules. */
private static final ManifestSection.Iterator INSTALL = new InstallIterator ();
/** Restores existing modules.
* Not private because ModuleInstaller uses it for the IDE manifest.
*/
static final ManifestSection.Iterator RESTORE = INSTALL;
/** Deinstalator of sections.
*/
private static final ManifestSection.Iterator REMOVE = new ManifestSection.Iterator () {
/** Process action section.
*/
public void processAction (ManifestSection.ActionSection as) throws InstantiationException {
ModuleActions.remove (as);
}
/** Processes the option section.
*/
public void processOption (ManifestSection.OptionSection os) throws InstantiationException {
NbControlPanel pool = NbControlPanel.getDefault ();
pool.remove (os.getOption ());
}
/** Processes the loader section.
*/
public void processLoader (ManifestSection.LoaderSection ls) throws InstantiationException {
DataLoader loader = ls.getLoader ();
LoaderPoolNode.remove (loader);
}
/** Processes debugger section
*/
public void processDebugger (ManifestSection.DebuggerSection ds) throws InstantiationException {
NbTopManager.setDebugger (null);
}
/** Processes executor section
*/
public void processService (ManifestSection.ServiceSection es) throws InstantiationException {
Services.removeService (es);
}
/** Processes filesystem section
*/
public void processFileSystem (ManifestSection.FileSystemSection fs) throws InstantiationException {
ModuleFSSection.uninstall (fs);
}
/** Processes environment node section.
* @exception Exception if there is an error and the section should be removed
*/
public void processNode (ManifestSection.NodeSection es) throws InstantiationException {
if (ManifestSection.NodeSection.TYPE_ROOTS.equalsIgnoreCase (es.getType ())) {
NbPlaces.removeRoot (es.getNode ());
} else if (ManifestSection.NodeSection.TYPE_SESSION.equalsIgnoreCase (es.getType ())) {
NbPlaces.removeSession (es.getNode ());
} else {
EnvironmentNode.removeNode (es.getNode ());
}
}
/** Processes clipboard convertor section.
* @exception Exception if there is an error and the section should be removed
*/
public void processClipboardConvertor (ManifestSection.ClipboardConvertorSection es) throws InstantiationException {
CoronaClipboard.removeConvertor (es.getConvertor ());
}
};
}
/*
* Log
* 45 Gandalf 1.44 1/16/00 Ian Formanek Removed semicolons after
* methods body to prevent fastjavac from complaining
* 44 Gandalf 1.43 1/13/00 Petr Hrebejk i18n
* 43 Gandalf 1.42 1/10/00 Jesse Glick #5252 -- making lack of
* helpset for module a nonfatal error.
* 42 Gandalf 1.41 1/5/00 Petr Hrebejk New module installer
* 41 Gandalf 1.40 12/2/99 Jesse Glick Loaders cannot be removed
* from pool, either intentionally or accidentally (e.g. after failed
* deserialize).
* 40 Gandalf 1.39 11/25/99 Jesse Glick Rewrite of
* LoaderPoolNode, specifically the management of loader ordering. Now
* permits multiple -before and -after dependencies, and should be more
* robust. Also made LoaderPoolItemNode's properly deletable and fixed a
* timing-related NullPointerException when uninstalling modules.
* 39 Gandalf 1.38 11/6/99 Jesse Glick You may now unwrap
* documentation (for OpenIDE-Module-Description) to $NBHOME/docs/
* preserving classpath (or $NBUSER/docs/).
* 38 Gandalf 1.37 10/27/99 Petr Hrebejk Testing of modules added
* 37 Gandalf 1.36 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems Copyright in File Comment
* 36 Gandalf 1.35 10/7/99 Petr Hrebejk
* 35 Gandalf 1.34 9/10/99 Jaroslav Tulach Services API.
* 34 Gandalf 1.33 9/3/99 Jaroslav Tulach Catches exception in
* closing.
* 33 Gandalf 1.32 8/1/99 Petr Hrebejk Multiuser install fixed
* 32 Gandalf 1.31 7/28/99 Jaroslav Tulach Additional manifest &
* separation of actions by modules
* 31 Gandalf 1.30 7/9/99 Jesse Glick ModuleHelpAction
* implemented.
* 30 Gandalf 1.29 6/8/99 Ian Formanek ---- Package Change To
* org.openide ----
* 29 Gandalf 1.28 5/27/99 Jaroslav Tulach Executors rearanged.
* 28 Gandalf 1.27 5/16/99 Jaroslav Tulach Throws FileNotFound
* instead of Zip exception
* 27 Gandalf 1.26 5/13/99 Jaroslav Tulach Services.
* 26 Gandalf 1.25 5/7/99 Jaroslav Tulach Help.
* 25 Gandalf 1.24 5/6/99 Jaroslav Tulach Should load also
* extensions.
* 24 Gandalf 1.23 5/4/99 Jaroslav Tulach Relative URL for modules.
* 23 Gandalf 1.22 5/4/99 Jaroslav Tulach Correct processing of
* executors.
* 22 Gandalf 1.21 4/28/99 Jaroslav Tulach XML storage for modules.
* 21 Gandalf 1.20 4/16/99 Libor Martinek
* 20 Gandalf 1.19 4/7/99 Ian Formanek Rename
* Section->ManifestSection
* 19 Gandalf 1.18 4/7/99 Ian Formanek Rename
* Description->ModuleDescription
* 18 Gandalf 1.17 3/30/99 Jaroslav Tulach Form loader before Java
* loaderem.
* 17 Gandalf 1.16 3/29/99 Jaroslav Tulach places ().nodes
* ().session ()
* 16 Gandalf 1.15 3/26/99 Jaroslav Tulach
* 15 Gandalf 1.14 3/26/99 Ian Formanek Fixed use of obsoleted
* NbBundle.getBundle (this)
* 14 Gandalf 1.13 3/25/99 Ian Formanek
* 13 Gandalf 1.12 3/25/99 Jaroslav Tulach Loader pool order fixed.
* 12 Gandalf 1.11 3/13/99 Jaroslav Tulach Places.roots ()
* 11 Gandalf 1.10 3/10/99 Jaroslav Tulach Modules from
* autoloadfolder are restored not initialized
* 10 Gandalf 1.9 3/8/99 Jesse Glick For clarity: Module ->
* ModuleInstall; NetBeans-Module-Main -> NetBeans-Module-Install.
* 9 Gandalf 1.8 2/25/99 Jaroslav Tulach Change of clipboard
* management
* 8 Gandalf 1.7 2/18/99 David Simonek initializer manifest
* added
* 7 Gandalf 1.6 1/27/99 Jaroslav Tulach
* 6 Gandalf 1.5 1/12/99 Jaroslav Tulach
* 5 Gandalf 1.4 1/12/99 Jaroslav Tulach Modules are loaded by
* URLClassLoader
* 4 Gandalf 1.3 1/11/99 Ian Formanek
* 3 Gandalf 1.2 1/6/99 Jaroslav Tulach Debugger is now module.
* 2 Gandalf 1.1 1/6/99 Ian Formanek Reflecting change in
* datasystem package
* 1 Gandalf 1.0 1/5/99 Ian Formanek
* $
*/